from decimal import Decimal
from django.http import HttpResponse
from django.shortcuts import render_to_response, redirect
from django.core.exceptions import ObjectDoesNotExist

from pyfacture import models
from pyfacture import message

import datetime
import json
import uuid
from pyfacture.pdf.InvoicePDFCreator import InvoicePDFCreator
from ..timesheet import utils

import logging

__author__ = 'Pierre Moermans'
logger = logging.getLogger(__name__)

class InvoiceDecoder(json.JSONDecoder):
    def decode(self, s):
        invoice_dict = json.loads(s)
        invoice = models.Invoice()
        invoice.client = models.Client.objects.get(id=int(invoice_dict['client']['id']))
        invoice.date = datetime.date.today()
        lines = []
        for json_line in invoice_dict['lines']:
            line = models.InvoiceLine()
            line.invoice = invoice
            line.description = json_line['description']
            line.quantity = Decimal(json_line['quantity'])
            line.unit_price = Decimal(json_line['unit_price'])
            line.vat = Decimal(str(json_line['vat']))
            lines.append(line)
        logger.debug("Decoded invoice " + str(invoice) + " and associated client " + str(invoice.client) + " to it")
        return invoice,lines

def _generate_view(request, invoice, error_msg=''):
    invoice.lines = invoice.invoiceline_set.all()
    invoice.client.address = getClientAddress(invoice.client, invoice.date)
    invoice.date = invoice.date.strftime("%d / %m / %Y")

    total = 0
    for line in invoice.lines:
        line.total = line.unit_price * line.quantity * (1 + line.vat)
        total += line.total
        line.total = "{0:.2f}".format(line.total)
        line.vat = "{0:02d}".format(int(line.vat * 100))
        line.quantity = "{0:.2f}".format(line.quantity)
        line.unit_price = "{0:.2f}".format(line.unit_price)

    if error_msg == '':
        error_msg = None

    return render_to_response('view_invoice.html', {
        'selected_invoice': invoice.number ,
        'invoice_numbers': getExistingInvoiceNumbers(),
        'invoice': invoice,
        'total': "{0:.2f}".format(total),
        'active_menu': 'invoice',
        'active_submenu': 'view',
        'new_link': '/invoice/new/',
        'view_link': '/invoice/view/',
        'error_msg': error_msg,
        'show_submenu': True,})

def view(request,invoice_number):
    logger.debug("User requested to view invoice " + invoice_number)
    if not request.user.is_authenticated():
        logger.info("User requested to view invoice " + invoice_number + " but is not authenticated")
        return render_to_response('logon.html', {'original_url': request.path})

    try:
        invoice = models.Invoice.objects.get(number=invoice_number)
    except ObjectDoesNotExist:
        try:
            invoice = models.Invoice.objects.latest('date')
        except ObjectDoesNotExist:
            logger.debug("invoice " + invoice_number + " does not exist. New invoice screen returned because there is no invoice in DB")
            return new(request)
        logger.debug("invoice " + invoice_number + " does not exist. The latest invoice " + invoice.number + " is returned")
        return _generate_view(request, invoice, "No such invoice")
        
    return _generate_view(request, invoice)

def view_default(request):
    logger.debug("User requested to view default invoice")
    if not request.user.is_authenticated():
        logger.debug("User requested to view default invoice but is not authenticated")
        return render_to_response('logon.html', {'original_url': request.path})

    try:
        invoice = models.Invoice.objects.latest('date')
    except ObjectDoesNotExist:
        logger.info("There is no default invoice. New screen is returned")
        return new(request)
    logger.debug("Returning default (latest) invoice " + invoice.number)
    return view(request,invoice.number)

def pdf(request, invoice_number):
    logger.debug("User requested PDF for invoice " + invoice_number)
    if not request.user.is_authenticated():
        logger.info("User requested PDF for invoice " + invoice_number + " but is not authenticated")
        return render_to_response('logon.html', {'original_url': request.path})

    try:
        invoice = models.Invoice.objects.get(number=invoice_number)
    except ObjectDoesNotExist:
        try:
            invoice = models.Invoice.objects.latest('date')
        except ObjectDoesNotExist:
            logger.info("The invoice " + invoice_number + " does not exist and there is no invoice in the system. The New invoice screen is returned")
            return new(request)
        logger.info("The invoice " + invoice_number + " does not exist. The view default invoice is returned with error message")
        return _generate_view(request, invoice, "No such invoice")

    invoice.lines = invoice.invoiceline_set.all()
    invoice.client.address = getClientAddress(invoice.client, invoice.date)

    logger.debug("invoice " + invoice_number + " found: " + str(invoice) + ". The client is " + str(invoice.client) + ". The address at that moment is " + str(invoice.client.address))

    response = HttpResponse(mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=facture-' + invoice_number + '.pdf'

    pdfCreator = InvoicePDFCreator(response)
    pdfCreator.writeLogo()
    pdfCreator.writeInvoiceData(number=invoice.number, date=invoice.date)
    pdfCreator.writeClientData(invoice.client, invoice.client.address)
    pdfCreator.writeInvoiceLines(invoice.lines)

    vat_only = sum([line.quantity * line.unit_price * line.vat for line in invoice.lines])
    total_with_vat = sum([line.quantity * line.unit_price * (1 + line.vat) for line in invoice.lines])

    pdfCreator.writeTotalLine(vat_only, total_with_vat)
    pdfCreator.writeFooter()

    pdfCreator.save()

    return response

def new(request):
    logger.debug("User requested the new invoice screen")
    if not request.user.is_authenticated():
        logger.info("User requested the new invoice screen but is not authenticated")
        return render_to_response('logon.html', {'original_url': request.path})

    availableTimesheets = models.Timesheet.objects.filter(contract__isnull = False)

    return render_to_response("new_invoice.html", {
        'today': datetime.date.today().strftime("%d / %m / %Y"),
        'active_menu': 'invoice',
        'active_submenu': 'new',
        'new_link': '/invoice/new/',
        'view_link': '/invoice/view/',
        'show_submenu': True,
        'availableTimesheets': utils.getTimesheetsForTemplate(availableTimesheets),
    })

def save(request):
    logger.debug("User requested to save a new invoice")
    if not request.user.is_authenticated():
        logger.info("User requested to save a new invoice but is not authenticated")
        return render_to_response('logon.html', {'original_url': request.path})
    if request.is_ajax():
        try:
            invoice,lines = InvoiceDecoder().decode(request.raw_post_data)
            invoice.number = nextInvoiceNumber()
            invoice.save()
            for line in lines:
                invoice.invoiceline_set.add(line)
            logger.info("The new invoice " + invoice.number + " has been saved with " + str(len(lines)) + " line(s)")
        except Exception, e:
            uid = uuid.uuid4()
            logger.error(message.msgWithUUID(uid, "Error saving invoice"),e)
            return message.buildError("Error saving invoice", uid)
        redirect_url = "/invoice/view/{0}/".format(invoice.number)
        return HttpResponse('{"redirect":"' + redirect_url + '"}', 'application/json')

    redirect_url = "/invoice/new/"
    return HttpResponse(redirect_url, 'text/html')

def nextInvoiceNumber():
    count_existing_inv_this_year = len(models.Invoice.objects.filter(date__year=datetime.date.today().year))
    next_invoice_number = '2011{0:04d}'.format(count_existing_inv_this_year + 1)
    logger.debug("next invoice number is " + next_invoice_number)
    return next_invoice_number


def getExistingInvoiceNumbers():
    numbers = []
    for invoice in models.Invoice.objects.all():
        numbers.append(invoice.number)
    logger.debug("existing invoice numbers: " + str(numbers))
    return numbers

def getClientAddress(client, point_in_time):
    try:
        address_link_at_point_in_time = models.ClientAddressLink.objects.filter(client__id__exact = client.id).filter(since__lte=point_in_time).latest('since')
    except models.ClientAddressLink.DoesNotExist as e:
        raise Exception("The client " + str(client) + " doesn't have an address at " + point_in_time.strftime('%d/%m/%Y'))
    logger.debug("address of client " + str(client) + " at " + point_in_time.strftime('%d/%m/%Y') + " is " + str(address_link_at_point_in_time.address))
    return address_link_at_point_in_time.address

